library StringIterator
/*
    Made by: edo494
    Version: 1.0
    
    Description:
        
        This piece of code allows you to forward iterate over words in strings,
        reading them out from the "container" depending on the delimiter.
        
    API:
        
        struct StringIterator
            
            static method create takes string container returns thistype
                - creates new instance
                
            method setDelims takes string delimiters returns nothing
                - sets the delimiters to characters in "delimiters"
                - example: "abc" will make the instance treat any
                  a, b or c inside the string as delimiter
                  
            method read takes nothing returns string
                - returns string containing charaters from "position"
                  up to "end" or until any delimiter is found
                  
            method peek takes nothing returns string
                - returns string containing charaters from "position"
                  up to "end" or until any delimiter is found
                - does not move "position" forward
                
            method nextDelim takes nothing returns string
                - returns upcomming delimiter pack
                - so if 2 or more delimiters after each other occur
                  this function returns all of them
                  
            method operator string= takes string newContainer returns nothing
                - sets internal container to "newContainer"
                - call to this function resets position
                  
            method operator start takes nothing returns boolean
                - returns whether the iterator is at the start or not
                
            method operator end takes nothing returns boolean
                - returns whether the iterator is at the end or not
                
            method operator size takes nothing returns integer
                - returns number of charaters inside the container
            
            method operator position takes nothing returns integer
                - returns the current position of the iterator inside container
                
            method operator position= takes integer newPos returns boolean
                - sets the position to the "newPos"
                - in debug mode, if <0 or >size, returns false
                  otherwise true
                - outside of debug mode, returns true
                
            method reset takes nothing returns nothing
                - returns the iterator to start

    Example:
    scope example initializer init
    
        private function t takes nothing returns boolean
            local string s = GetEventPlayerChatString()
            local StringIterator iter = StringIterator.create(s)
            call iter.setDelims(" ")
            call BJDebugMsg("Message inserted by player:\n")
            loop
                exitwhen iter.end
                call BJDebugMsg(iter.read())
            endloop
            return false
        endfunction
        
        private function init takes nothing returns nothing
            local trigger tr = CreateTrigger()
            call TriggerRegisterPlayerChatEvent(tr, Player(0), "", false)
            call TriggerAddCondition(tr, Condition(function t))
            set tr = null
        endfunction
    endscope            
*/
    struct StringIterator
        private string myStr
        private string myDelims
        private integer pointer
        private integer sSize
        private integer dSize
        
        static method create takes string s returns thistype
            local thistype this = allocate()
            set this.myStr = s
            set this.myDelims = " "
            set this.pointer = 0
            set this.dSize = StringLength(this.myDelims)
            set this.sSize = StringLength(s)
            return this
        endmethod

        static method createEx takes string container, string delims returns thistype
            local thistype this = allocate()
            set this.myStr = container
            set this.myDelims = delims
            set this.pointer = 0
            set this.dSize = StringLength(delims)
            set this.sSize = StringLength(container)
            return this
        endmethod
        
        method setDelims takes string s returns nothing
            set this.myDelims = s
            set this.dSize = StringLength(s)
        endmethod
        
        method operator start takes nothing returns boolean
            return this.pointer <= 0
        endmethod
        
        method operator end takes nothing returns boolean
            return this.pointer >= this.sSize
        endmethod
        
        method operator size takes nothing returns integer
            return this.sSize
        endmethod
        
        method operator position takes nothing returns integer
            return this.pointer
        endmethod
        
        private method isDelim takes string s returns boolean
            local integer i = this.dSize
            local string delims = this.myDelims
            if s == null then
                return true
            endif
            loop
                exitwhen i == 0
                if s == SubString(delims, i-1, i) then
                    return true
                endif
                set i = i - 1
            endloop
            return false
        endmethod
        
        private method readBool takes boolean b returns string
            local string helper = ""
            local string ths = this.myStr
            local integer size = this.sSize
            local integer current = this.pointer
            local string cont = ""
            //evade all remaining delimiters from the last read
            loop
                if current == size then
                    exitwhen true
                endif
                exitwhen not isDelim(SubString(ths, current, current+1))
                set current = current + 1
            endloop
            //fill up the string
            loop
                exitwhen current == size
                set helper = SubString(ths, current, current+1)
                exitwhen isDelim(helper)
                set cont = cont + helper
                set current = current + 1
            endloop
            if b then
                set this.pointer = current
            endif
            //return the string
            return cont
        endmethod
        
        method read takes nothing returns string
            return readBool(true)
        endmethod
        
        method peek takes nothing returns string
            return readBool(false)
        endmethod
        
        method nextDelim takes nothing returns string
            local string ths = this.myStr
            local integer size = this.sSize
            local integer current = this.pointer
            local string s = ""
            local string q = ""
            if current == size then
                return ""
            endif
            //evade all non delimiters
            loop
                set q = SubString(ths, current, current+1)
                exitwhen current == size or not isDelim(q)
                set current = current + 1
            endloop
            //read next delimiters
            loop
                set q = SubString(ths, current, current+1)
                exitwhen current == size or not isDelim(q)
                set s = s + q
                set current = current + 1
            endloop
            //return them
            return s
        endmethod
        
        method operator position= takes integer newPosition returns boolean
            debug if newPosition > this.size or newPosition < 0 then
                debug return false
            debug endif
            set this.pointer = newPosition
            return true
        endmethod
        
        method reset takes nothing returns nothing
            set this.pointer = 0
        endmethod
        
        method operator string= takes string s returns nothing
            set this.myStr = s
            set this.sSize = StringLength(s)
            set this.pointer = 0
        endmethod
    endstruct
endlibrary